package com.smyx.juc;

import java.time.LocalDateTime;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author SmyxCoding
 * @date 2022-08-11 20:41
 **/
public class CompletableFutureTest {
    private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
            5, 50, 10L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(),
            r -> new Thread(r, "custom-thread-" + new AtomicInteger(1).getAndIncrement()),
            new ThreadPoolExecutor.AbortPolicy()
    );

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("BEG >>> " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now());
//        oneByOnV1();
//        oneByOnV2();
//        taskAfterEither();
//        taskBothFinish();
        multiTask();
        System.out.println("END >>> " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now());
    }

    private static void multiTask() throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> 100, executor);
        CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> 200, executor);
        CompletableFuture<Integer> f3 = CompletableFuture.supplyAsync(() -> 300, executor);
        // 全部完成
        CompletableFuture<Void> all = CompletableFuture.allOf(f1, f2, f3);
        // 阻塞任務 其完成才可以進行後續的代碼邏輯 或者用 all.join();
        all.get();

        // 單個完成即可
        CompletableFuture<Object> any = CompletableFuture.anyOf(f1, f2, f3);
        any.get();
    }

    // 多个一起执行（必须全部完成）然后汇总
    private static void taskBothFinish() throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("one beg: " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("one end: " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now());
            return 100;
        }, executor);

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("two beg: " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("two end: " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now());
            return 100;
        }, executor);

        // 都執行完成後執行
//        future1.thenAcceptBothAsync(future2, (r1, r2) -> {
//            System.out.println("r1 = " + r1);
//            System.out.println("r2 = " + r2);
//        }, executor);

        // 都執行完後 在處理
        CompletableFuture<Integer> lastFuture = future1.thenCombineAsync(future2, (r1, r2) -> {
            System.out.println("r11 = " + r1);
            System.out.println("r22 = " + r2);
            return r1 * r2;
        }, executor);

        // 獲取處理結果 lastFuture.get()
        System.out.println("lastFuture>>>>> " + lastFuture.get() + " " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now());
    }

    // 多个一起执行（其中之一完成即可）然后汇总
    private static void taskAfterEither() {
        CompletableFuture<Integer> one = CompletableFuture.supplyAsync(() -> {
            System.out.println("one beg: " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("one end: " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now());
            return 100;
        }, executor);

        CompletableFuture<Integer> two = CompletableFuture.supplyAsync(() -> {
            System.out.println("two beg: " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("two end: " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now());
            return 100;
        }, executor);

        one.runAfterEitherAsync(two, () -> {
            System.out.println("EitherAsync : " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now());
        });
    }

    // 顺序执行 操作返回结果
    private static void oneByOnV2() {
        // supplyAsync 有返回结果 后续线程需要用 Accept 接收
        CompletableFuture.supplyAsync(
            () -> {
                System.out.println("one : " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                return 100;
            },
            executor
        ).thenApplyAsync( // 再次计算必须使用 thenApplyAsync
            (res) -> {
                System.out.println("two : " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now() + " : 前一个线程的返回结果res >>> " + res);
                return res * 100;
            }, executor
        ).thenAcceptAsync( // 直接接收结果
            (res) -> System.out.println("three : " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now() + " : 前一个线程的返回结果res >>> " + res),
            executor
        );
    }

    // 顺序执行 无返回结果
    private static void oneByOnV1() {
        // runAsync 没有返回结果的
        CompletableFuture.runAsync(
            () -> {
                System.out.println("one : " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            },
            executor
        ).thenRunAsync(
            () -> System.out.println("two : " + Thread.currentThread().getName() + " >>> " + LocalDateTime.now()),
            executor
        );
    }
}
